//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// C# Port port by: Lars Brubaker
//                  larsbrubaker@gmail.com
// Copyright (C) 2007
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;

using AGG.VertexSource;
using AGG.Transform;

namespace AGG.UI
{
    public class UndoBuffer
    {
        public enum MergeType { Mergable, NotMergable };

        public interface IUndoData
        {
            IUndoData Clone();
        }

        class UndoCheckPoint
        {
            internal IUndoData m_ObjectToUndoTo;
            internal string m_TypeOfObject;
            internal MergeType m_MergeType;

            internal UndoCheckPoint(IUndoData objectToUndoTo, string typeOfObject, MergeType mergeType)
            {
                m_ObjectToUndoTo = objectToUndoTo;
                m_TypeOfObject = typeOfObject;
                m_MergeType = mergeType;
            }
        };

        List<UndoCheckPoint> m_UndoBuffer = new List<UndoCheckPoint>();
        int m_CurrentUndoIndex = -1;
        int m_LastValidUndoIndex = -1;

        public UndoBuffer()
        {

        }

        public void Add(IUndoData objectToUndoTo, string typeOfObject, MergeType mergeType)
        {
            IUndoData cloneableObject = objectToUndoTo;
            if(cloneableObject != null)
            {
                if (m_CurrentUndoIndex <= 0
                    || mergeType == MergeType.NotMergable
                    || m_UndoBuffer[m_CurrentUndoIndex].m_TypeOfObject != typeOfObject)
                {
                    m_CurrentUndoIndex++;
                }

                UndoCheckPoint newUndoCheckPoint = new UndoCheckPoint(cloneableObject.Clone(), typeOfObject, mergeType);
                if (m_CurrentUndoIndex < m_UndoBuffer.Count)
                {
                    m_UndoBuffer[m_CurrentUndoIndex] = newUndoCheckPoint;
                }
                else
                {
                    m_UndoBuffer.Add(newUndoCheckPoint);
                }
                
                m_LastValidUndoIndex = m_CurrentUndoIndex;
            }
        }

        public object GetPrevUndoObject()
        {
            if(m_CurrentUndoIndex > 0)
            {
                return m_UndoBuffer[--m_CurrentUndoIndex].m_ObjectToUndoTo.Clone();
            }

            return null;
        }

        public object GetNextRedoObject()
        {
            if (m_LastValidUndoIndex > m_CurrentUndoIndex)
            {
                m_CurrentUndoIndex++;
                return m_UndoBuffer[m_CurrentUndoIndex].m_ObjectToUndoTo.Clone();
            }

            return null;
        }
    };

    //------------------------------------------------------------------------
    public class TextEditWidget : GUIWidget
    {
        double m_BorderSize;
        double m_Thickness;
        double m_CapsHeight;
        UndoBuffer m_UndoBuffer = new UndoBuffer();

        bool m_Selecting;
        int m_SelectionIndexToStartBefore;
        int m_CharIndexToInsertBefore;
        int m_CharIndexToAcceptAsMerging;

        Vector2D m_BarPosition;

        double m_DesiredBarX;

        TextWidget m_TextWidget;
        bool isMultiLine = true;

        bool m_MouseOverTextArea = false;

        public delegate void TextChangedEventHandler(object sender, EventArgs e);
        public event TextChangedEventHandler TextChanged;

        internal class UndoData : UndoBuffer.IUndoData
        {
            private UndoData(String undoString, int charIndexToInsertBefore, int selectionIndexToStartBefore, bool selecting)
            {
                m_String = undoString;
                m_Selecting = selecting;
                m_SelectionIndexToStartBefore = selectionIndexToStartBefore;
                m_CharIndexToInsertBefore = charIndexToInsertBefore;
            }

            internal UndoData(TextEditWidget textEditWidget)
            {
                m_String = textEditWidget.m_TextWidget.Text;
                m_CharIndexToInsertBefore = textEditWidget.m_CharIndexToInsertBefore;
                m_SelectionIndexToStartBefore = textEditWidget.m_SelectionIndexToStartBefore;
                m_Selecting = textEditWidget.m_Selecting;
            }

            public UndoBuffer.IUndoData Clone()
            {
                UndoData clonedUndoData = new UndoData(m_String, m_CharIndexToInsertBefore, m_SelectionIndexToStartBefore, m_Selecting);
                return clonedUndoData;
            }

            String m_String;
            bool m_Selecting;
            int m_SelectionIndexToStartBefore;
            int m_CharIndexToInsertBefore;

            internal void ExtractData(TextEditWidget textEditWidget)
            {
                textEditWidget.m_TextWidget.Text = m_String;
                textEditWidget.m_CharIndexToInsertBefore = m_CharIndexToInsertBefore;
                textEditWidget.m_SelectionIndexToStartBefore = m_SelectionIndexToStartBefore;
                textEditWidget.m_Selecting = m_Selecting;
            }
        };

        public override String Text
        {
            get
            {
                return m_TextWidget.Text;
            }

            set
            {
                if (m_TextWidget.Text != value)
                {
                    m_CharIndexToInsertBefore = 0;
                    m_TextWidget.Text = value;
                    Invalidate();
                }
            }
        }

        public TextEditWidget(string text, int pixelWidth, int pixelHeight, double capitalHeight)
        {
            Bounds = new rect_d(0, 0, pixelWidth, pixelHeight);

            m_TextWidget = new TextWidget(text, capitalHeight);
            //m_TextWidget.SetTransform(Affine.NewTranslation(0, 30));
            AddChild(m_TextWidget);

            m_CharIndexToInsertBefore = text.Length;

            m_BorderSize = capitalHeight * .2;
            m_Thickness = capitalHeight / 8;
            m_CapsHeight = capitalHeight;

            FixBarPosition(true);

            UndoData newUndoData = new UndoData(this);
            m_UndoBuffer.Add(newUndoData, "Initial", UndoBuffer.MergeType.NotMergable);
        }

        public bool Multiline
        {
            get 
            {
                return isMultiLine;
            }

            set
            {
                isMultiLine = value;
            }
        }

        public override void OnDraw(Graphics2D graphics2D)
        {
            if (m_Selecting
                && m_SelectionIndexToStartBefore != m_CharIndexToInsertBefore)
            {
                Vector2D selectPosition = m_TextWidget.Printer.GetOffsetLeftOfCharacterIndex(m_SelectionIndexToStartBefore);

				rect_d bar = new rect_d (Math.Ceiling(selectPosition.x),
				            Math.Ceiling(selectPosition.y), 
				            Math.Ceiling(selectPosition.x + 1), 
				            Math.Ceiling(selectPosition.y + 10));
                RoundedRect selectCursorRect = new RoundedRect(bar, 0);
                graphics2D.Render(selectCursorRect, new RGBA_Bytes(0, 1.0, 1.0));
            }

            rect_d boundsPlusPoint5 = Bounds;
            boundsPlusPoint5.Inflate(-.5);
            RoundedRect borderRect = new RoundedRect(boundsPlusPoint5, 0);
            conv_stroke borderLine = new conv_stroke(borderRect);
            if (m_MouseOverTextArea)
            {
                graphics2D.Render(borderLine, new RGBA_Doubles(0, 0, .4).GetAsRGBA_Bytes());
            }
            else
            {
                graphics2D.Render(borderLine, new RGBA_Bytes(0, 0, 0));
            }

			rect_d bar2 = new rect_d(Math.Ceiling(m_BarPosition.x), 
			                        Math.Ceiling(m_BarPosition.y), 
			                        Math.Ceiling(m_BarPosition.x + 1), 
			                        Math.Ceiling(m_BarPosition.y + 10));
            RoundedRect cursorRect = new RoundedRect(bar2, 0);

            graphics2D.Render(cursorRect, new RGBA_Bytes(0, 0, 1.0));

            base.OnDraw(graphics2D);
        }

        bool m_MouseIsDown = false;
        public override void OnMouseDown(MouseEventArgs mouseEvent)
        {
            m_CharIndexToInsertBefore = m_TextWidget.Printer.GetCharacterIndexToStartBefore(new Vector2D(mouseEvent.X, mouseEvent.Y));
            m_SelectionIndexToStartBefore = m_CharIndexToInsertBefore;
            m_Selecting = true;
            m_MouseIsDown = true;

            base.OnMouseDown(mouseEvent);

            FixBarPosition(true);
        }

        public override void OnMouseMove(MouseEventArgs mouseEvent)
        {
            if (Bounds.hit_test(mouseEvent.X, mouseEvent.Y))
            {
                m_MouseOverTextArea = true;
            }
            else
            {
                m_MouseOverTextArea = false;
            }
            if (m_MouseIsDown)
            {
                m_SelectionIndexToStartBefore = m_TextWidget.Printer.GetCharacterIndexToStartBefore(new Vector2D(mouseEvent.X, mouseEvent.Y));
                Invalidate();
            }
            base.OnMouseMove(mouseEvent);
        }

        public override void OnMouseUp(MouseEventArgs mouseEvent)
        {
            m_MouseIsDown = false;
            base.OnMouseUp(mouseEvent);
        }

        public override string ToString()
        {
            return m_TextWidget.Text;
        }

        void FixBarPosition(bool SetDesiredBarPosition)
        {
            m_BarPosition = m_TextWidget.Printer.GetOffsetLeftOfCharacterIndex(m_CharIndexToInsertBefore);
            if(SetDesiredBarPosition)
            {
                m_DesiredBarX = m_BarPosition.x;
            }
            Invalidate();
        }

        private void DeleteIndex(int startIndexInclusive)
        {
            DeleteIndexRange(startIndexInclusive, startIndexInclusive);
        }

        private void DeleteIndexRange(int startIndexInclusive, int endIndexInclusive)
        {
            int LengthToDelete = (endIndexInclusive + 1) - startIndexInclusive;
            if (LengthToDelete > 0)
            {
                StringBuilder stringBuilder = new StringBuilder(m_TextWidget.Text);
                stringBuilder.Remove(startIndexInclusive, LengthToDelete);
                m_TextWidget.Text = stringBuilder.ToString();
            }
        }

        private void DeleteSelection()
        {
            DeleteSelection(true);
        }

        private void DeleteSelection(bool createUndoMarker)
        {
            if (m_Selecting)
            {
                if (m_CharIndexToInsertBefore < m_SelectionIndexToStartBefore)
                {
                    DeleteIndexRange(m_CharIndexToInsertBefore, m_SelectionIndexToStartBefore - 1);
                }
                else
                {
                    DeleteIndexRange(m_SelectionIndexToStartBefore, m_CharIndexToInsertBefore - 1);
                    m_CharIndexToInsertBefore = m_SelectionIndexToStartBefore;
                }

                if (createUndoMarker)
                {
                    UndoData newUndoDeleteData = new UndoData(this);
                    m_UndoBuffer.Add(newUndoDeleteData, "Delete", UndoBuffer.MergeType.NotMergable);
                }

                m_Selecting = false;
            }
        }

        public override void OnKeyDown(KeyEventArgs keyEvent)
        {
            bool SetDesiredBarPosition = true;
            bool turnOffSelection = false;
            if (keyEvent.Shift)
            {
                if (!m_Selecting)
                {
                    m_Selecting = true;
                    m_SelectionIndexToStartBefore = m_CharIndexToInsertBefore;
                }
            }
            else if (m_Selecting)
            {
                turnOffSelection = true;
            }

            switch (keyEvent.KeyCode)
            {
                case Keys.Escape:
                    if (m_Selecting)
                    {
                        turnOffSelection = true;
                        keyEvent.SuppressKeyPress = true;
                        keyEvent.Handled = true;
                    } 
                    break;

                case Keys.Left:
                    if(keyEvent.Control)
                    {
                        GotoBeginingOfPreviousToken();
                    }
                    else if (m_CharIndexToInsertBefore > 0)
                    {
                        if (turnOffSelection)
                        {
                            m_CharIndexToInsertBefore = Math.Min(m_CharIndexToInsertBefore, m_SelectionIndexToStartBefore);
                        }
                        else
                        {
                            m_CharIndexToInsertBefore--;
                        }
                    }
                    keyEvent.SuppressKeyPress = true;
                    keyEvent.Handled = true;
                    break;

                case Keys.Right:
                    if (keyEvent.Control)
                    {
                        GotoBeginingOfNextToken();
                    }
                    else if (m_CharIndexToInsertBefore < m_TextWidget.Text.Length)
                    {
                        if (turnOffSelection)
                        {
                            m_CharIndexToInsertBefore = Math.Max(m_CharIndexToInsertBefore, m_SelectionIndexToStartBefore);
                        }
                        else
                        {
                            m_CharIndexToInsertBefore++;
                        }
                    }
                    keyEvent.SuppressKeyPress = true;
                    keyEvent.Handled = true;
                    break;

                case Keys.Up:
                    if (turnOffSelection)
                    {
                        m_CharIndexToInsertBefore = Math.Min(m_CharIndexToInsertBefore, m_SelectionIndexToStartBefore);
                    }
                    GotoLineAbove();
                    SetDesiredBarPosition = false;
                    keyEvent.SuppressKeyPress = true;
                    keyEvent.Handled = true;
                    break;

                case Keys.Down:
                    if (turnOffSelection)
                    {
                        m_CharIndexToInsertBefore = Math.Max(m_CharIndexToInsertBefore, m_SelectionIndexToStartBefore);
                    }
                    GotoLineBelow();
                    SetDesiredBarPosition = false;
                    keyEvent.SuppressKeyPress = true;
                    keyEvent.Handled = true;
                    break;

                case Keys.Space:
                    keyEvent.Handled = true;
                    break;

                case Keys.End:
                    if (keyEvent.Control)
                        m_CharIndexToInsertBefore = m_TextWidget.Text.Length;
                    else
                        GotoEndOfCurrentLine();
                    keyEvent.SuppressKeyPress = true;
                    keyEvent.Handled = true;
                    break;

                case Keys.Home:
                    if (keyEvent.Control)
                        m_CharIndexToInsertBefore = 0;
                    else
                        GotoStartOfCurrentLine();
                    keyEvent.SuppressKeyPress = true;
                    keyEvent.Handled = true;
                    break;

                case Keys.Back:
                    if (!m_Selecting
                        && m_CharIndexToInsertBefore > 0)
                    {
                        m_SelectionIndexToStartBefore = m_CharIndexToInsertBefore - 1;
                        m_Selecting = true;
                    }

                    DeleteSelection();

                    keyEvent.Handled = true;
                    keyEvent.SuppressKeyPress = true;
                    break;

                case Keys.Delete:
                    if (keyEvent.Shift)
                    {
                        CopySelection();
                        DeleteSelection();
                        keyEvent.Handled = true;
                        keyEvent.SuppressKeyPress = true;
                    }
                    else
                    {
                        if (!m_Selecting
                        && m_CharIndexToInsertBefore < m_TextWidget.Text.Length)
                        {
                            m_SelectionIndexToStartBefore = m_CharIndexToInsertBefore + 1;
                            m_Selecting = true;
                        }

                        DeleteSelection();
                    }

                    turnOffSelection = true;
                    keyEvent.Handled = true;
                    keyEvent.SuppressKeyPress = true;
                    break;

                case Keys.Enter:
                    if (!Multiline)
                    {
                        // TODO: do the right thing.
                        keyEvent.Handled = true;
                        keyEvent.SuppressKeyPress = true;

                        OnTextChanged();
                    }
                    break;

                case Keys.Insert:
                    if (keyEvent.Shift)
                    {
                        turnOffSelection = true;
                        PasteFromClipboard();
                        keyEvent.Handled = true;
                        keyEvent.SuppressKeyPress = true;
                    }
                    if(keyEvent.Control)
                    {
                        turnOffSelection = false;
                        CopySelection();
                        keyEvent.Handled = true;
                        keyEvent.SuppressKeyPress = true;
                    }
                    break;

                case Keys.X:
                    if (keyEvent.Control)
                    {
                        CopySelection();
                        DeleteSelection();
                        keyEvent.Handled = true;
                        keyEvent.SuppressKeyPress = true;
                    }
                    break;

                case Keys.C:
                    if (keyEvent.Control)
                    {
                        turnOffSelection = false;
                        CopySelection();
                        keyEvent.Handled = true;
                        keyEvent.SuppressKeyPress = true;
                    }
                    break;

                case Keys.V:
                    if (keyEvent.Control)
                    {
                        PasteFromClipboard();
                        keyEvent.Handled = true;
                        keyEvent.SuppressKeyPress = true;
                    }
                    break;

                case Keys.Z:
                    if (keyEvent.Control)
                    {
                        UndoData undoData = (UndoData)m_UndoBuffer.GetPrevUndoObject();
                        if(undoData != null)
                        {
                            undoData.ExtractData(this);
                        }
                        keyEvent.Handled = true;
                        keyEvent.SuppressKeyPress = true;
                    }
                    break;

                case Keys.Y:
                    if (keyEvent.Control)
                    {
                        UndoData undoData = (UndoData)m_UndoBuffer.GetNextRedoObject();
                        if (undoData != null)
                        {
                            undoData.ExtractData(this);
                        }
                        keyEvent.Handled = true;
                        keyEvent.SuppressKeyPress = true;
                    }
                    break;
            }

            base.OnKeyDown(keyEvent);

            FixBarPosition(SetDesiredBarPosition);

            // if we are not going to type a character, and therefore replace the selection, turn off the selection now if needed.
            if(keyEvent.SuppressKeyPress && turnOffSelection)
            {
                m_Selecting = false;
            }

            Invalidate();
        }

        protected virtual void OnTextChanged()
        {
            if (TextChanged != null)
            {
                TextChanged(this, null);
            }
        }

        private void CopySelection()
        {
            if (m_Selecting)
            {
                if (m_CharIndexToInsertBefore < m_SelectionIndexToStartBefore)
                {
#if SILVERLIGHT
                    throw new NotImplementedException();
#else
                    GuiHalSurface.ClipboardSetText(m_TextWidget.Text.Substring(m_CharIndexToInsertBefore, m_SelectionIndexToStartBefore - m_CharIndexToInsertBefore));
#endif
                }
                else
                {
#if SILVERLIGHT
                    throw new NotImplementedException();
#else
                    GuiHalSurface.ClipboardSetText(m_TextWidget.Text.Substring(m_SelectionIndexToStartBefore, m_CharIndexToInsertBefore - m_SelectionIndexToStartBefore));
#endif
                }
            }
            else if (Multiline)
            {
                // copy the line?
            }
        }

        private void PasteFromClipboard()
        {
#if SILVERLIGHT
                    throw new NotImplementedException();
#else
            if (GuiHalSurface.ClipboardContainsText())
            {
                if (m_Selecting)
                {
                    DeleteSelection(false);
                }

                StringBuilder stringBuilder = new StringBuilder(m_TextWidget.Text);
                String stringOnClipboard = GuiHalSurface.ClipboardGetText();
                stringBuilder.Insert(m_CharIndexToInsertBefore, stringOnClipboard);
                m_CharIndexToInsertBefore += stringOnClipboard.Length;
                m_TextWidget.Text = stringBuilder.ToString();

                UndoData newUndoData = new UndoData(this);
                m_UndoBuffer.Add(newUndoData, "Paste", UndoBuffer.MergeType.NotMergable);
            }
#endif
        }

        public override void OnKeyPress(KeyPressEventArgs keyPressEvent)
        {
            if (m_Selecting)
            {
                DeleteSelection();
                m_Selecting = false;
            }

            StringBuilder tempString = new StringBuilder(m_TextWidget.Text);
            tempString.Insert(m_CharIndexToInsertBefore, keyPressEvent.KeyChar.ToString());
            keyPressEvent.Handled = true;
            m_CharIndexToInsertBefore++;
            m_TextWidget.Text = tempString.ToString();
            base.OnKeyPress(keyPressEvent);

            FixBarPosition(true);

            UndoData newUndoData = new UndoData(this);
            if (m_CharIndexToAcceptAsMerging == m_CharIndexToInsertBefore - 1 
                && keyPressEvent.KeyChar != '\r')
            {
                m_UndoBuffer.Add(newUndoData, "Typing", UndoBuffer.MergeType.Mergable);
            }
            else
            {
                m_UndoBuffer.Add(newUndoData, "Typing", UndoBuffer.MergeType.NotMergable);
            }
            m_CharIndexToAcceptAsMerging = m_CharIndexToInsertBefore;
        }

        private int GetIndexOffset(int CharacterStartIndexInclusive, int MaxCharacterEndIndexInclusive, double DesiredPixelOffset)
        {
            int OffsetIndex = 0;
            int EndOffsetIndex = MaxCharacterEndIndexInclusive - CharacterStartIndexInclusive;
            Vector2D offset = new Vector2D();
            Vector2D lastOffset = new Vector2D();
            while (true)
            {
                m_TextWidget.Printer.GetSize(CharacterStartIndexInclusive, CharacterStartIndexInclusive + OffsetIndex, out offset);
                OffsetIndex++;
                if (offset.x >= DesiredPixelOffset || OffsetIndex >= EndOffsetIndex)
                {
                    if (Math.Abs(offset.y) < .01 
                        && Math.Abs(lastOffset.x - DesiredPixelOffset) < Math.Abs(offset.x - DesiredPixelOffset))
                    {
                        OffsetIndex--;
                    }
                    break;
                }
                lastOffset = offset;
            }

            int MaxLength = Math.Min(MaxCharacterEndIndexInclusive - CharacterStartIndexInclusive, OffsetIndex);
            return CharacterStartIndexInclusive + MaxLength;
        }

        // the '\r' is always considered to be the end of the line.
        // if startIndexInclusive == endIndexInclusive, the line is empty (other than the return)
        private void GetLineIndexesAboutCharIndex(int charIndexToGetLineIndexesFor, out int startIndexInclusive, out int endIndexInclusive)
        {
            startIndexInclusive = 0;
            endIndexInclusive = m_TextWidget.Text.Length;

            charIndexToGetLineIndexesFor = Math.Max(Math.Min(charIndexToGetLineIndexesFor, m_TextWidget.Text.Length), 0);
            // first lets find the end of the line.  Check if we are on a '\r'
            if (charIndexToGetLineIndexesFor == m_TextWidget.Text.Length
                || m_TextWidget.Text[charIndexToGetLineIndexesFor] == '\r')
            {
                // we are on the end of the line
                endIndexInclusive = charIndexToGetLineIndexesFor;
            }
            else
            {
                int endReturn = m_TextWidget.Text.IndexOf('\r', charIndexToGetLineIndexesFor + 1);
                if (endReturn != -1)
                {
                    endIndexInclusive = endReturn;
                }
            }

            // check if the line is empty (the character to our left is the '\r' on the previous line
            if (m_TextWidget.Text[endIndexInclusive - 1] == '\r')
            {
                // the line is empty the start = the end.
                startIndexInclusive = endIndexInclusive;
            }
            else
            {
                int returnAtStartOfCurrentLine = m_TextWidget.Text.LastIndexOf('\r', endIndexInclusive - 1);
                if (returnAtStartOfCurrentLine != -1)
                {
                    startIndexInclusive = returnAtStartOfCurrentLine + 1;
                }
            }
        }

        private void GotoLineAbove()
        {
            int startIndexInclusive;
            int endIndexInclusive;
            GetLineIndexesAboutCharIndex(m_CharIndexToInsertBefore, out startIndexInclusive, out endIndexInclusive);

            int prevStartIndexInclusive;
            int prevEndIndexInclusive;
            GetLineIndexesAboutCharIndex(startIndexInclusive - 1, out prevStartIndexInclusive, out prevEndIndexInclusive);
            // we found the extents of the line above now put the cursor in the right place.
            m_CharIndexToInsertBefore = GetIndexOffset(prevStartIndexInclusive, prevEndIndexInclusive, m_DesiredBarX);
        }

        private void GotoLineBelow()
        {
            int startIndexInclusive;
            int endIndexInclusive;
            GetLineIndexesAboutCharIndex(m_CharIndexToInsertBefore, out startIndexInclusive, out endIndexInclusive);

            int nextStartIndexInclusive;
            int nextEndIndexInclusive;
            GetLineIndexesAboutCharIndex(endIndexInclusive + 1, out nextStartIndexInclusive, out nextEndIndexInclusive);
            // we found the extents of the line above now put the cursor in the right place.
            m_CharIndexToInsertBefore = GetIndexOffset(nextStartIndexInclusive, nextEndIndexInclusive, m_DesiredBarX);
        }

        private void GotoBeginingOfNextToken()
        {
            if(m_CharIndexToInsertBefore == m_TextWidget.Text.Length)
            {
                return;
            }

            bool SkippedWiteSpace = false;
            if (m_TextWidget.Text[m_CharIndexToInsertBefore] == '\r')
            {
                m_CharIndexToInsertBefore++;
                SkippedWiteSpace = true;
            }
            else
            {
                Regex firstWhiteSpaceRegex = new Regex("\\s");
                Match firstWhiteSpace = firstWhiteSpaceRegex.Match(m_TextWidget.Text, m_CharIndexToInsertBefore);
                if (firstWhiteSpace.Success)
                {
                    SkippedWiteSpace = true;
                    m_CharIndexToInsertBefore = firstWhiteSpace.Index;
                }
            }

            if (SkippedWiteSpace)
            {
                Regex firstNonWhiteSpaceRegex = new Regex("[^\\t ]");
                Match firstNonWhiteSpace = firstNonWhiteSpaceRegex.Match(m_TextWidget.Text, m_CharIndexToInsertBefore);
                if(firstNonWhiteSpace.Success)
                {
                    m_CharIndexToInsertBefore = firstNonWhiteSpace.Index;
                }
            }
            else
            {
                GotoEndOfCurrentLine();
            }
        }

        private void GotoBeginingOfPreviousToken()
        {
            if (m_CharIndexToInsertBefore == 0)
            {
                return;
            }

            Regex firstNonWhiteSpaceRegex = new Regex("[^\\t ]", RegexOptions.RightToLeft);
            Match firstNonWhiteSpace = firstNonWhiteSpaceRegex.Match(m_TextWidget.Text, m_CharIndexToInsertBefore);
            if (firstNonWhiteSpace.Success)
            {
            	if(m_TextWidget.Text[firstNonWhiteSpace.Index] == '\r')
            	{
            		if(firstNonWhiteSpace.Index < m_CharIndexToInsertBefore-1)
	            	{
	            		m_CharIndexToInsertBefore = firstNonWhiteSpace.Index;
	            		return;
	            	}
	            	else
	            	{
			            firstNonWhiteSpaceRegex = new Regex("[^\\t\\r ]", RegexOptions.RightToLeft);
			            firstNonWhiteSpace = firstNonWhiteSpaceRegex.Match(m_TextWidget.Text, m_CharIndexToInsertBefore);
			            if (firstNonWhiteSpace.Success)
			            {
	                		m_CharIndexToInsertBefore = firstNonWhiteSpace.Index;
			            }
	            	}
            	}
            	else
            	{
	                m_CharIndexToInsertBefore = firstNonWhiteSpace.Index;
            	}

                Regex firstWhiteSpaceRegex = new Regex("\\s", RegexOptions.RightToLeft);
                Match firstWhiteSpace = firstWhiteSpaceRegex.Match(m_TextWidget.Text, m_CharIndexToInsertBefore);
                if (firstWhiteSpace.Success)
                {
                    m_CharIndexToInsertBefore = firstWhiteSpace.Index+1;
                }
                else
                {
                    GotoStartOfCurrentLine();
                }
            }
        }

        private void GotoEndOfCurrentLine()
        {
            int indexOfReturn = m_TextWidget.Text.IndexOf('\r', m_CharIndexToInsertBefore);
            if(indexOfReturn == -1)
            {
                m_CharIndexToInsertBefore = m_TextWidget.Text.Length;
            }
            else
            {
                m_CharIndexToInsertBefore = indexOfReturn;
            }
        }

        private void GotoStartOfCurrentLine()
        {
            if (m_CharIndexToInsertBefore > 0)
            {
                int indexOfReturn = m_TextWidget.Text.LastIndexOf('\r', m_CharIndexToInsertBefore - 1);
                if (indexOfReturn == -1)
                {
                    m_CharIndexToInsertBefore = 0;
                }
                else
                {
		            Regex firstNonWhiteSpaceRegex = new Regex("[^\\t ]");
		            Match firstNonWhiteSpace = firstNonWhiteSpaceRegex.Match(m_TextWidget.Text, indexOfReturn+1);
		            if (firstNonWhiteSpace.Success)
		            {
		            	if(firstNonWhiteSpace.Index < m_CharIndexToInsertBefore
		            	   || m_TextWidget.Text[m_CharIndexToInsertBefore-1] == '\r')
			            {
		            		m_CharIndexToInsertBefore = firstNonWhiteSpace.Index;
		            		return;
			            }
		            }

		            m_CharIndexToInsertBefore = indexOfReturn+1;
                }
            }
        }

        public override bool InRect(double x, double y)
        {
            return Bounds.hit_test(x,y);
        }
    };
}
